unit libusb;

{$mode objfpc}{$H+}

interface

uses ctypes;

const
  {$ifdef WINDOWS}
   {$ifdef CPUI386}
   LibName = 'libusb0_x86.dll';
   {$else CPUI386}
   LibName = 'libusb0.dll';
   {$endif CPUI386}
  {$endif WINDOWS}

   LIBUSB_PATH_MAX = 512;

const
   USB_CLASS_PER_INTERFACE = 0;  (* for DeviceClass  *)
   USB_CLASS_AUDIO       = 1;
   USB_CLASS_COMM        = 2;
   USB_CLASS_HID         = 3;
   USB_CLASS_PRINTER     = 7;
   USB_CLASS_MASS_STORAGE = 8;
   USB_CLASS_HUB         = 9;
   USB_CLASS_DATA        = 10;
   USB_CLASS_VENDOR_SPEC = $ff;

   USB_DT_DEVICE    = $01;
   USB_DT_CONFIG    = $02;
   USB_DT_STRING    = $03;
   USB_DT_INTERFACE = $04;
   USB_DT_ENDPOINT  = $05;
   USB_DT_HID       = $21;
   USB_DT_REPORT    = $22;
   USB_DT_PHYSICAL  = $23;
   USB_DT_HUB       = $29;

type
   usb_descriptor_header = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
   end;

   usb_string_descriptor = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
      wData: array [0..0] of cushort;
   end;

   (* HID descriptor  *)

   usb_hid_descriptor = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
      bcdHID: cushort;
      bCountryCode: cuchar;
      bNumDescriptors: cuchar;
   end;

(* Endpoint descriptor  *)

const
   USB_MAXENDPOINTS = 32;

type
   usb_endpoint_descriptor = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
      bEndpointAddress: cuchar;
      bmAttributes: cuchar;
      wMaxPacketSize: cushort;
      bInterval: cuchar;
      bRefresh: cuchar;
      bSynchAddress: cuchar;
      extra: pcuchar;  (* Extra descriptors  *)
      extralen: cint;
   end;

const
   USB_ENDPOINT_ADDRESS_MASK     = $0f;  (* in bEndpointAddress  *)
   USB_ENDPOINT_DIR_MASK         = $80;
   USB_ENDPOINT_TYPE_MASK        = $03;  (* in bmAttributes  *)
   USB_ENDPOINT_TYPE_CONTROL     = 0;
   USB_ENDPOINT_TYPE_ISOCHRONOUS = 1;
   USB_ENDPOINT_TYPE_BULK        = 2;
   USB_ENDPOINT_TYPE_INTERRUPT   = 3;
   (* Interface descriptor  *)

   USB_MAXINTERFACES = 32;

type
   Pusb_endpoint_descriptor = ^usb_endpoint_descriptor;

   usb_interface_descriptor = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
      bInterfaceNumber: cuchar;
      bAlternateSetting: cuchar;
      bNumEndpoints: cuchar;
      bInterfaceClass: cuchar;
      bInterfaceSubClass: cuchar;
      bInterfaceProtocol: cuchar;
      iInterface: cuchar;
      endpoint: Pusb_endpoint_descriptor;
      extra: pcuchar;  (* Extra descriptors  *)
      extralen: cint;
   end;

const
   USB_MAXALTSETTING = 128;  (* Hard limit  *)

type
   Pusb_interface_descriptor = ^usb_interface_descriptor;

   usb_interface = packed record
      altsetting: Pusb_interface_descriptor;
      num_altsetting: cint;
   end;

(* Configuration descriptor information..  *)

const
   USB_MAXCONFIG = 8;

type
   Pusb_interface = ^usb_interface;

   usb_config_descriptor = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
      wTotalLength: cushort;
      bNumInterfaces: cuchar;
      bConfigurationValue: cuchar;
      iConfiguration: cuchar;
      bmAttributes: cuchar;
      MaxPower: cuchar;
      interface_: Pusb_interface;
      extra: pcuchar;  (* Extra descriptors  *)
      extralen: cint;
   end;

   (* Device descriptor  *)

   usb_device_descriptor = packed record
      bLength: cuchar;
      bDescriptorType: cuchar;
      bcdUSB: cushort;
      bDeviceClass: cuchar;
      bDeviceSubClass: cuchar;
      bDeviceProtocol: cuchar;
      bMaxPacketSize0: cuchar;
      idVendor: cushort;
      idProduct: cushort;
      bcdDevice: cushort;
      iManufacturer: cuchar;
      iProduct: cuchar;
      iSerialNumber: cuchar;
      bNumConfigurations: cuchar;
   end;

   usb_ctrl_setup = packed record
      bRequestType: cuchar;
      bRequest: cuchar;
      wValue: cushort;
      wIndex: cushort;
      wLength: cushort;
   end;

  (*
 * Standard requests
  *)

const
   USB_REQ_GET_STATUS    = $00;
   USB_REQ_CLEAR_FEATURE = $01;
   (* 0x02 is reserved  *)

   USB_REQ_SET_FEATURE = $03;
   (* 0x04 is reserved  *)

   USB_REQ_SET_ADDRESS = $05;
   USB_REQ_GET_DESCRIPTOR = $06;
   USB_REQ_SET_DESCRIPTOR = $07;
   USB_REQ_GET_CONFIGURATION = $08;
   USB_REQ_SET_CONFIGURATION = $09;
   USB_REQ_GET_INTERFACE = $0A;
   USB_REQ_SET_INTERFACE = $0B;
   USB_REQ_SYNCH_FRAME = $0C;
   USB_TYPE_STANDARD   = ($00 shl 5);
   USB_TYPE_CLASS      = ($01 shl 5);
   USB_TYPE_VENDOR     = ($02 shl 5);
   USB_TYPE_RESERVED   = ($03 shl 5);
   USB_RECIP_DEVICE    = $00;
   USB_RECIP_INTERFACE = $01;
   USB_RECIP_ENDPOINT  = $02;
   USB_RECIP_OTHER     = $03;
  (*
 * Various libusb API related stuff
  *)

   USB_ENDPOINT_IN  = $80;
   USB_ENDPOINT_OUT = $00;
   (* Error codes  *)

   USB_ERROR_BEGIN = 500000;
  (*
 * This is supposed to look weird. This file is generated from autoconf
 * and I didn't want to make this too complicated.
  *)

  (*
 * Device reset types for usb_reset_ex.
 * http://msdn.microsoft.com/en-us/library/ff537269%28VS.85%29.aspx
 * http://msdn.microsoft.com/en-us/library/ff537243%28v=vs.85%29.aspx
  *)

   USB_RESET_TYPE_RESET_PORT = (1 shl 0);
   USB_RESET_TYPE_CYCLE_PORT = (1 shl 1);
   USB_RESET_TYPE_FULL_RESET = (USB_RESET_TYPE_CYCLE_PORT or USB_RESET_TYPE_RESET_PORT);

type
   PPusb_device = ^Pusb_device;
   Pusb_device = ^Tusb_device;
   Pusb_config_descriptor = ^usb_config_descriptor;
   Pusb_bus = ^usb_bus;

   Tusb_device = packed record
      Next: Pusb_device;
      prev: Pusb_device;
      filename: array [0..LIBUSB_PATH_MAX - 1] of char;
      bus: Pusb_bus;
      descriptor: usb_device_descriptor;
      config: Pusb_config_descriptor;
      dev: Pointer;  (* Darwin support  *)
      devnum: cuchar;
      num_children: cuchar;
      children: PPusb_device;
   end;

   usb_bus = packed record
      Next: Pusb_bus;
      prev: Pusb_bus;
      dirname: array [0..LIBUSB_PATH_MAX - 1] of char;
      devices: Pusb_device;
      location: culong;
      root_dev: Pusb_device;
   end;

   usb_dev_handle = packed record
   end;

   Pusb_dev_handle = ^usb_dev_handle;

function usb_open(dev: Pusb_device): Pusb_dev_handle; cdecl; external LibName;
function usb_close(dev: Pusb_dev_handle): cint; cdecl; external LibName;
function usb_get_string(dev: Pusb_dev_handle; index_, langid: cint; buf: PChar; buflen: size_t): cint; cdecl; external LibName;
function usb_get_string_simple(dev: Pusb_dev_handle; index_: cint; buf: PChar; buflen: size_t): cint; cdecl; external LibName;

(* descriptors.c  *)
function usb_get_descriptor_by_endpoint(udev: Pusb_dev_handle; ep: cint; type_, index_: cuchar; buf: Pointer; size: cint): cint; cdecl; external LibName;
function usb_get_descriptor(udev: Pusb_dev_handle; type_, index_: cuchar; buf: Pointer; size: cint): cint; cdecl; external LibName;

(* <arch>.c  *)
function usb_bulk_write(dev: Pusb_dev_handle; ep: cint; bytes: PChar; size, timeout: cint): cint; cdecl; external LibName;
function usb_bulk_read(dev: Pusb_dev_handle; ep: cint; bytes: PChar; size, timeout: cint): cint; cdecl; external LibName;
function usb_interrupt_write(dev: Pusb_dev_handle; ep: cint; bytes: PChar; size, timeout: cint): cint; cdecl; external LibName;
function usb_interrupt_read(dev: Pusb_dev_handle; ep: cint; bytes: PChar; size, timeout: cint): cint; cdecl; external LibName;
function usb_control_msg(dev: Pusb_dev_handle; requesttype, request, Value, index_: cint; bytes: PChar; size, timeout: cint): cint; cdecl; external LibName;
function usb_set_configuration(dev: Pusb_dev_handle; configuration: cint): cint; cdecl; external LibName;
function usb_claim_interface(dev: Pusb_dev_handle; interface_: cint): cint; cdecl; external LibName;
function usb_release_interface(dev: Pusb_dev_handle; interface_: cint): cint; cdecl; external LibName;
function usb_set_altinterface(dev: Pusb_dev_handle; alternate: cint): cint; cdecl; external LibName;
function usb_resetep(dev: Pusb_dev_handle; ep: cuint): cint; cdecl; external LibName;
function usb_clear_halt(dev: Pusb_dev_handle; ep: cuint): cint; cdecl; external LibName;
function usb_reset(dev: Pusb_dev_handle): cint; cdecl; external LibName;
function usb_reset_ex(dev: Pusb_dev_handle; reset_type: cuint): cint; cdecl; external LibName;
function usb_strerror: PChar; cdecl; external LibName;
procedure usb_init; cdecl; external LibName;
procedure usb_set_debug(level: cint); cdecl; external LibName;
function usb_find_busses: cint; cdecl; external LibName;
function usb_find_devices: cint; cdecl; external LibName;

function usb_device(dev: Pusb_dev_handle): Pusb_device; cdecl; external LibName;
function usb_get_busses: Pusb_bus; cdecl; external LibName;

function usb_find_device(VID, PID: word; index: longint): Pusb_dev_handle;

function usb_get_string(dev: Pusb_dev_handle; index_, langid: cint): string;

implementation

function usb_find_device(VID, PID: word; index: longint): Pusb_dev_handle;
var bus: Pusb_bus;
    dev: Pusb_device;
begin
   result := nil;

   usb_find_busses;
   usb_find_devices;

   bus := usb_get_busses;
   while assigned(bus) do
   begin
      dev := bus^.devices;
      while assigned(dev) do
      begin
         if (dev^.descriptor.idVendor = VID) and (dev^.descriptor.idProduct = PID) then
         begin
            if index <= 0 then
               exit(usb_open(dev))
            else
               dec(index);
         end;

         dev := dev^.Next;
      end;
      bus := bus^.Next;
   end;
end;

function usb_get_string(dev: Pusb_dev_handle; index_, langid: cint): string;
var buf: array[0..1023] of char;
    t: cint;
begin
   result := '';

   t := usb_get_string(dev, index_, langid, @buf[0], length(buf));
   if t > 0 then
   begin
      buf[t] := #0;
      result := pchar(@buf[0]);
   end;
end;

end.

